package mercurius.fitness.client.util;

import java.util.HashMap;
import java.util.Set;
import java.util.Vector;

import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.Response;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.user.client.rpc.AsyncCallback;

import net.pleso.framework.client.bl.exceptions.FrameworkRuntimeException;
import net.pleso.framework.client.dal.SelectParams;
import net.pleso.framework.client.dal.db.types.DBInteger;
import mercurius.fitness.client.Fitness;
import mercurius.fitness.client.rb.diet.Diet;
import mercurius.fitness.client.rb.exercise.Exercise;
import mercurius.fitness.client.rb.measurement.Measurements;
import mercurius.fitness.client.rb.summary.Summary;
import mercurius.fitness.client.rb.user.User;

public class MercuriusHelper {

	public static final String MERCURIUS_UPDATE = "update_data";
	public static final String MERCURIUS_INSERT = "add_data";
	public static final String MERCURIUS_DELETE = "delete_data";
	public static final String MERCURIUS_SELECT = "select_data";
	public static final String MERCURIUS_SELECT_BY_ID = "select_one_data";
	public static final String MERCURIUS_SELECT_COUNT = "select_count_data";
	
	public MercuriusHelper() {
		
		
	}
	
	private static String generateXmlData(Object o,boolean delete) {
		Object[] d = {o};
		return generateXmlData(d,delete);
	}
	
	private static String generateXmlData(Object o) {
		Object[] d = {o};
		return generateXmlData(d,false);
	}
	
	private static String generateXmlData(Object[] d,boolean delete) {
		String data = "";
		data += "<data>" +
		"<sessions>";

		for(int i=0;i<d.length;i++) {
			data += "<session>";
			if(d[i] instanceof Measurements) {
				data += dailyData((Measurements) d[i],delete);
			} else if(d[i] instanceof Exercise) {
				data += exerciseData((Exercise) d[i],delete);
			} else if(d[i] instanceof User) {
				data += userData((User) d[i],delete);
			} else if(d[i] instanceof Diet) {
				data += dietData((Diet) d[i],delete);
			} // else if(d[i] instanceof Resistance) {
				
			//}
			data += "</session>";
		}
		
		data += "</sessions>" +
				"</data>";
		
		return data;
	}
	
	public static void updateMercurius(Object obj,String action) {
		updateMercurius(obj,action,null);
	}
	
	public static void updateMercurius(Object obj,String action,AsyncCallback callback) {
		
		AbstractResponseCommand dummycommand = new AbstractResponseCommand() {
			   public void execute() {
			   }

			public Object getObject() {
				return null;
			}

			public void setObject(Object obj) {
				
			}
			 };
		
		updateMercurius(obj,action,callback,dummycommand,dummycommand);
	}
	
	public static void updateMercurius(Object obj,String action,AsyncCallback callback,AbstractResponseCommand success) {
		
		AbstractResponseCommand dummycommand = new AbstractResponseCommand() {
			   public void execute() {
			   }

			public Object getObject() {
				return null;
			}

			public void setObject(Object obj) {
				
			}
			 };
		
		updateMercurius(obj,action,callback,success,dummycommand);
	}
	
	public static void updateMercurius(Object obj,final String action,final AsyncCallback callback,final AbstractResponseCommand success,final AbstractResponseCommand fail) {
		String actiontype = "";
		Vector entries = new Vector();
		
		if(obj instanceof Measurements) {
			actiontype = "measurements";
		} else if(obj instanceof Exercise) {
			actiontype = "exercise";
		} else if(obj instanceof User) {
			actiontype = "user";
		} else if(obj instanceof Summary) {
			actiontype = "summary";
		} else if(obj instanceof Diet) {
			actiontype = "diet";
			entries.add(new Entry("diettype", ((Diet)obj).getType()));
		}// else if(obj instanceof Resistance) {
		//	actiontype = "resistance";
		//}
		
		
		if(action.equals(MERCURIUS_DELETE)) {
			entries.add(new Entry("data", MercuriusHelper.generateXmlData(obj,true)));
			entries.add(new Entry("single_entry", "true"));
		} else if(action.equals(MERCURIUS_UPDATE)) {
			entries.add(new Entry("data", MercuriusHelper.generateXmlData(obj)));
			entries.add(new Entry("single_entry", "true"));
		} else if(action.equals(MERCURIUS_INSERT)) {
			entries.add(new Entry("data", MercuriusHelper.generateXmlData(obj)));
			entries.add(new Entry("single_entry", "true"));
			entries.add(new Entry("return_id", "true"));
		} else if(action.equals(MERCURIUS_SELECT)) {
			SelectParams params = (SelectParams) success.getObject();
			entries.add(new Entry("offset", ""+params.getOffset()));
			entries.add(new Entry("limit", ""+params.getLimit()));
			entries.add(new Entry("orderByColumnName", params.getOrderByColumnName()));
			entries.add(new Entry("orderByDirection", ""+params.getOrderByDirection()));
			
		} else if(action.equals(MERCURIUS_SELECT_COUNT)) {
		} else if(action.equals(MERCURIUS_SELECT_BY_ID)) {
			Integer id = (Integer) success.getObject();
			entries.add(new Entry("id", id.toString()));
		}
		
		entries.add(new Entry("action",action));
		
		if(Fitness.get().getFeedKey().length() > 0) {
			entries.add(new Entry("feedkey",Fitness.get().getFeedKey()));
		}
		
		if(Fitness.get().getAdminFeedKey().length() > 0) {
			entries.add(new Entry("adminfeedkey",Fitness.get().getAdminFeedKey()));
		}
		
		entries.add(new Entry("type", actiontype));

		final String successMsg = "Executed action successfully: "+action;
		final String failedMsg = "Executed action with errors: "+action;
		
		Logger.debug("Executing action: "+action);
		
		HTTPUtil.doPost(Fitness.get().getURL(),entries.toArray(),new RequestCallback() {

	          public void onError(Request request, Throwable exception) {
	        	  String msg = "Error when trying to execute action " + action;
	        	  Logger.error(msg,exception);
	        	  if(callback != null) {
	        		  callback.onFailure(new FrameworkRuntimeException(msg, exception));
    				}
	          }
	          
	          public void onResponseReceived(Request request, Response response) {
	        	  String r = response.getText();
	        	  Logger.debug("Received Response: "+ r);
	        	  if(r.startsWith("OK:")) {
	        		  String results = r.substring(r.indexOf(":")+1,r.length());
	        		  boolean isnumber = isNumber(results);
	        		  int num = Integer.parseInt(results);
	        		  
	        		  if((isnumber && 
	        				((
	        			        ((action.equals(MERCURIUS_DELETE) || action.equals(MERCURIUS_UPDATE)) && num == 1) || 
	        			        ((action.equals(MERCURIUS_INSERT) && num > 0) || (action.equals(MERCURIUS_SELECT_COUNT)) && num >= 0)
	        				)))) {
	        			  
	        			  Logger.debug(successMsg);
		      				success.execute(request,response,results);
		      				if(callback != null) {
		      					if(action.equals(MERCURIUS_SELECT_COUNT)) {
		      						callback.onSuccess(new Integer(num));
		      					} else {
		      						callback.onSuccess(new Integer(1));
		      					}
		      				}
	        		  } else {
	        				String msg = "Received an 'OK' result when executing action " + action+", but could not process:\n\t" + results;
	        				Logger.debug("Connection failed. " + msg);
	        				Logger.error(response.getText());
	        				Logger.debug(failedMsg);
			  				fail.execute(request,response,results);
			  				if(callback != null) {
			  					callback.onFailure(new FrameworkRuntimeException(msg));
		      				}
	        			}
		  			} else if(r.startsWith("{")) {
		  				String results = r;
		  				if(action.equals(MERCURIUS_SELECT)) {
		  					SelectParams params = (SelectParams) success.getObject();
		  					int offset = params.getOffset();
		 				   int limit = params.getLimit();
		 				   
		 				   JSONObject jsonObject = JSONParser.parse(results).isObject();
							String countstr = jsonObject.get("count").isString().stringValue();
							int count = (Integer.valueOf(countstr)).intValue();
							if(count == 0) { // No rows returned
								success.setObject(new Vector());
								return;
							}
		 				    JSONObject jsondata = jsonObject.get("data").isObject();
		 				    
		 				   if (offset > count - 1)
		 						offset = count - 1;
		 					
		 					if (offset + limit > count - 1)
		 						limit =  count - offset;
		 					
		 					Vector props = new Vector(limit);
		 					
		 					for (int i = 0; i < limit; i++){
		 						JSONObject jsonmeasurement = jsondata.get("" + (i + 1)).isObject();
		 						HashMap p = new HashMap();
		 						Set s = jsonmeasurement.keySet();
		 						Object[] a = s.toArray();
		 						for(int k=0;k<a.length;k++) {
		 							String key = (String) a[k];
		 							String val = jsonmeasurement.get(key).isString().stringValue();
		 							p.put(key,val);
		 						}
		 						props.add(p);
		 					}

		 					success.setObject(props);
	        		  } else if(action.equals(MERCURIUS_SELECT_BY_ID)) {
		  					JSONObject jsonObject = JSONParser.parse(results).isObject();
							JSONObject jsondata = jsonObject.get("data").isObject();
							HashMap p = new HashMap();
							JSONObject jsonmeasurement = jsondata.get("1").isObject();
							Set s = jsonmeasurement.keySet();
	 						Object[] a = s.toArray();
	 						for(int k=0;k<a.length;k++) {
	 							String key = (String) a[k];
	 							String val = jsonmeasurement.get(key).isString().stringValue();
	 							p.put(key,val);
	 						}
	 						
	 						success.setObject(p);
	        		  } else {
	        				String msg = "Received results when executing action " + action+", but could not process:\n\t" + results;
	        				Logger.debug("Connection failed. "+msg);
	        				Logger.error(response.getText());
	        				Logger.debug(failedMsg);
			  				fail.execute(request,response,results);
			  				if(callback != null) {
			  					callback.onFailure(new FrameworkRuntimeException(msg));
		      				}
	        			}
		  			} else {
		  				String msg = "Did not retrieve an 'OK' result when executing action " + action+".  Recieved:\n\t" + response.getText();
		  				Logger.debug("Connection failed. "+msg);
		  				Logger.error(response.getText());
		  				Logger.debug(failedMsg);
		  				fail.execute(request,response,r);
		  				if(callback != null) {
		  					callback.onFailure(new FrameworkRuntimeException(msg));
	      				}
		  			}
		    }
		});
	}

	public static boolean isNumber(String r) {
		boolean isnumber = false;
  	  int num =0;
  	  try {
			num = Integer.parseInt(r);
			return true;
		}
		catch(NumberFormatException e) {
			isnumber = false;
		}
		return isnumber;
	}
	
	private static String dailyData(Measurements measurements,boolean delete) {
		String xml = "";
		xml += "<sessiondaily>";
		if(measurements.getID() != DBInteger.nullValue) {
			xml += "<id>" + measurements.getID().toString() + "</id>";
		}
		
		if(!delete) {
			String date = DateTimeFormat.getFormat("yyyyy-MM-dd").format(measurements.getDate());
			xml += "<date>" + date + "</date>";
			if(measurements.getWeight() != null) {
				xml += "<weight>" + measurements.getWeight().toString()+ "</weight>";
			}
			if(measurements.getFat() != null) {
				xml += "<fat>" +measurements.getFat().toString() + "</fat>";
			}
			if(measurements.getNotes() != null) {
				xml += "<notes>" + measurements.getNotes().toString()+ "</notes>";
			}
			if(measurements.getRestingPulse() != null) {
				xml += "<restingpulse>" +measurements.getRestingPulse().toString() + "</restingpulse>";
			}
			if(measurements.getMilestone() != null) {
				xml += "<milestone>" +measurements.getMilestone().toString() + "</milestone>";
			}
		}
		xml += "</sessiondaily>";
		return xml;
	}
	
	private static String userData(User user,boolean delete) {
		String xml = "";
		xml += "<user>";
		if(user.getID() != DBInteger.nullValue) {
			xml += "<id>" + user.getID().toString() + "</id>";
		}
		
		if(!delete) {
			String dob = DateTimeFormat.getFormat("yyyyy-MM-dd").format(user.getDob());
			xml += "<dob>" + dob + "</dob>";
			xml += "<gender>" + user.getGender().intValue()+ "</gender>";
			xml += "<height>" +user.getHeight().floatValue() + "</height>";
			xml += "<height_unit>" + user.getHeightUnitString()+ "</height_unit>";
			xml += "<feed_key>" +user.getFeedkey() + "</feed_key>";
			xml += "<adminfeed_key>" +user.getAdminFeedkey() + "</adminfeed_key>";
			
		}
		xml += "</user>";
		return xml;
	}
	
	private static String dietData(Diet diet,boolean delete) {
		String xml = "";
		xml += "<sessiondiet>";
		if(diet.getID() != DBInteger.nullValue) {
			xml += "<id>" + diet.getID().toString() + "</id>";
		}
		
		
		
		if(!delete) {
			/*diet.getType()
			String dob = DateTimeFormat.getFormat("yyyyy-MM-dd").format(user.getDob());
			xml += "<dob>" + dob + "</dob>";
			xml += "<gender>" + user.getGender().intValue()+ "</gender>";
			xml += "<height>" +user.getHeight().floatValue() + "</height>";
			xml += "<height_unit>" + user.getHeightUnitString()+ "</height_unit>";
			xml += "<feed_key>" +user.getFeedkey() + "</feed_key>";
			xml += "<adminfeed_key>" +user.getAdminFeedkey() + "</adminfeed_key>";
			*/
		}
		xml += "</sessiondiet>";
		return xml;
	}
	
	private static String exerciseData(Exercise exer,boolean delete) {
		String xml = "";
		xml += "<sessioninfo>";
		if(exer.getID() != DBInteger.nullValue) {
			xml += "<id>" + exer.getID().toString() + "</id>";
		}
		
		if(!delete) {
			String date = DateTimeFormat.getFormat("yyyyy-MM-dd").format(exer.getDate());
			xml += "<date>" + date + "</date>";
			String datetime = DateTimeFormat.getFormat("yyyyy-MM-dd HH:mm:ss").format(exer.getDate());
			xml += "<datetime>" + datetime + "</datetime>";
			if(exer.getDuration() != null) {
				xml += "<duration>" + exer.getDuration() + "</duration>";
			}
			if(exer.getDistance() != null) {
				xml += "<distance>" + exer.getDistance()+ "</distance>";
			}
			if(exer.getDistanceUnitString() != null) {
				
				xml += "<distance_unit>" +exer.getDistanceUnitString() + "</distance_unit>";
			}
			if(exer.getAvgHR() != null) {
				xml += "<avg_hr>" + exer.getAvgHR() + "</avg_hr>";
			}
			if(exer.getMaxHR() != null) {
				xml += "<max_hr>" + exer.getMaxHR()+ "</max_hr>";
			}
			
			if(exer.getAvgSpeed() != null) {
				xml += "<avg_speed>" + exer.getAvgSpeed() + "</avg_speed>";
			}
			if(exer.getBestSpeed() != null) {
				xml += "<max_speed>" + exer.getBestSpeed()+ "</max_speed>";
			}
			
			if(exer.getAvgPace() != null) {
				xml += "<avg_pace>" + exer.getAvgSpeed() + "</avg_pace>";
			}
			if(exer.getBestSpeed() != null) {
				xml += "<best_pace>" + exer.getBestPace()+ "</best_pace>";
			}
			
			if(exer.getSpeedUnitString() != null) {
				xml += "<speed_unit>" +exer.getSpeedUnitString() + "</speed_unit>";
			}
			if(exer.getZ1Time() != null) {
				xml += "<z1_time>" + exer.getZ1Time() + "</z1_time>";
			} else {
				xml += "<z1_time>00:00:00</z1_time>";
			}
			if(exer.getZ2Time() != null) {
				xml += "<z2_time>" + exer.getZ2Time() + "</z2_time>";
			} else {
				xml += "<z2_time>00:00:00</z2_time>";
			}
			if(exer.getZ3Time() != null) {
				xml += "<z3_time>" + exer.getZ3Time() + "</z3_time>";
			} else {
				xml += "<z3_time>00:00:00</z3_time>";
			}
			if(exer.getZ4Time() != null) {
				xml += "<z4_time>" + exer.getZ4Time() + "</z4_time>";
			} else {
				xml += "<z4_time>00:00:00</z4_time>";
			}
			if(exer.getZ5Time() != null) {
				xml += "<z5_time>" + exer.getZ5Time() + "</z5_time>";
			} else {
				xml += "<z5_time>00:00:00</z5_time>";
			}
			
			if(exer.getActivity() != null) {
				xml += "<activity>" + exer.getActivity()+ "</activity>";
			}

			if(exer.getNotes() != null) {
				xml += "<notes>" +exer.getNotes() + "</notes>";
			}
			
			if(exer.getTitle() != null) {
				xml += "<title>" +exer.getTitle() + "</title>";
			}
			
			if(exer.getLocation() != null) {
				xml += "<location>" + exer.getLocation() + "</location>";
			}
			
			if(exer.getPublic() != null) {
				xml += "<public>" + exer.getPublic()+ "</public>";
			}
		}
		xml += "</sessioninfo>";
		return xml;
	}
}
